/* LIBDGL -- a Directed Graph Library implementation
 * Copyright (C) 2002 Roberto Micarelli
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* best view tabstop=4
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>

#include "../type.h"
#include "../graph.h"

#include "opt.h"


extern int errno;

/* If the clipper function returns 1 , the node is discarded and
 * the traversing of the graph toward its direction is abandoned.
 * Try to change the return value to 1 and see that the program
 * will not find any more paths to destinations.
 * The clipper receives data relating the network segment being examinated.
 * The pvarg is a user pointer registered to dglShortestPath() and
 * passed back to the clipper.
 * As a demo, the main uses the ClipperContext_s structure to store a nodeid
 * that must be discarded during the search. The clipper receives
 * that pointer and checks every node against the one specified there.
 * If the node matches return 1 , otherwise return 0.
 */

typedef struct {
	dglInt32_t node_to_discard;
} ClipperContext_s;

static int 	clipper 	(
						dglGraph_s * 	pgraph ,
						dglSPClipInput_s * pIn ,
						dglSPClipOutput_s * pOut ,
						void * 			pvarg		/* caller's pointer */
						)
{
	ClipperContext_s * pclip = (ClipperContext_s*) pvarg;
#if 0
	dglInt32_t * pnFromXYZ = (dglInt32_t*) dglNodeGet_Attr( pgraph, pIn->pnNodeFrom );
	dglInt32_t * pnToXYZ   = (dglInt32_t*) dglNodeGet_Attr( pgraph, pIn->pnNodeTo );

	printf( "clipper called:\n" );
	printf( "        from node: %ld - attributes x=%ld y=%ld z=%ld\n",
		dglNodeGet_Id(pgraph, pIn->pnNodeFrom), pnFromXYZ[0], pnFromXYZ[1], pnFromXYZ[2]);
	printf( "        to   node: %ld - attributes x=%ld y=%ld z=%ld\n",
		dglNodeGet_Id(pgraph, pIn->pnNodeTo), pnToXYZ[0], pnToXYZ[1], pnToXYZ[2]);
	printf( "        edge     : %ld\n",
		dglEdgeGet_Id(pgraph, pIn->pnEdge) );
#endif

	if ( pclip )
	{
		if ( dglNodeGet_Id(pgraph, pIn->pnNodeTo) == pclip->node_to_discard )
		{
			/*
			printf( "        discarder.\n" );
			*/
			return 1;
		}
	}
	/*
	printf( "        accepted.\n" );
	*/
	return 0;
}



int main( int argc , char ** argv )
{
	dglGraph_s  		graph;
	dglInt32_t 			from , to;

	int					fd , nret , version;
	dglSPReport_s * 	pReport;
	dglInt32_t			nDistance;
	dglSPCache_s		spCache;
	ClipperContext_s	clipctx , * pclipctx;

	/* program options
	 */
 	char	*	pszFilein;
 	char	*	pszFrom;
 	char	*	pszTo;
 	char	*	pszDiscard;
 	char	*	pszVersion;
	Boolean		fDistance;
	Boolean		fUnflatten;
 
	GNO_BEGIN /*short  	long        default     variable        help */
 	GNO_OPTION( "g", 	"graph", 	NULL ,  	& pszFilein ,	"graph file to view" )
 	GNO_OPTION( "v", 	"version", 	NULL , 		& pszVersion ,	"alter graph version" )
 	GNO_OPTION( "f", 	"from", 	NULL ,  	& pszFrom ,		"from-node id" )
 	GNO_OPTION( "t", 	"to", 		NULL ,  	& pszTo ,		"to-node id" )
 	GNO_OPTION( "d", 	"discard", 	NULL ,  	& pszDiscard ,	"node to discard in clipper" )
 	GNO_SWITCH( "D", 	"distance",	False , 	& fDistance ,	"Report shortest distance only" )
 	GNO_SWITCH( "U", 	"unflatten",False ,		& fUnflatten ,	"Unflatten the graph before processing" )
 	GNO_END
 

	if ( GNO_PARSE( argc , argv ) < 0 )
	{
		return 1;
	}
	/* options parsed
	 */

	if ( pszFilein == NULL || pszFrom == NULL || pszTo == NULL )
	{
		GNO_HELP( "incomplete parameters" );
		return 1;
	}

	if ( pszDiscard )
	{
		clipctx.node_to_discard = atol( pszDiscard );
		pclipctx = & clipctx;
	}
	else
		pclipctx = NULL;

	if ( pszVersion ) {
		version = atoi(pszVersion);
	}

	fd = open( pszFilein , O_RDONLY );
	if ( fd < 0 )
	{	
		perror( "open" ); return 1;
	}

	nret = dglRead( & graph , fd );

	close( fd );

	if ( nret < 0 )
	{
		fprintf( stderr , "dglRead error: %s\n" , dglStrerror( & graph ) );
		return 1;
	}

	if ( fUnflatten ) dglUnflatten( &graph );

	if ( pszVersion ) dglSet_Version(&graph, version);

	from = atol( pszFrom );
	to = atol( pszTo );

	printf( "shortest path: from-node %ld - to-node %ld\n\n" , from , to );

	dglInitializeSPCache( & graph, & spCache );

	if ( fDistance == False ) {
		nret = dglShortestPath( & graph , & pReport , from , to , clipper , pclipctx , & spCache );

		if ( nret == 0 )
		{
			printf( "destination node is unreachable\n\n" );
		}
		else if ( nret < 0 ) 
		{
			fprintf( stderr , "dglShortestPath error: %s\n", dglStrerror( & graph ) );
		}
		else
		{
			int i;
	
			printf( "shortest path report: total edges %ld - total distance %ld\n" , pReport->cArc , pReport->nDistance );
	
			for( i = 0 ; i < pReport->cArc ; i ++ )
			{
				printf(	"edge[%d]: from %ld to %ld - travel cost %ld - user edgeid %ld - distance from start node %ld\n" ,
						i,
						pReport->pArc[i].nFrom,
						pReport->pArc[i].nTo,
						dglEdgeGet_Cost(&graph, pReport->pArc[i].pnEdge), /* this is the cost from clip() */
						dglEdgeGet_Id(&graph, pReport->pArc[i].pnEdge),
						pReport->pArc[i].nDistance
						);
			}
			dglFreeSPReport( & graph , pReport );
		}
	}
	else {
		nret = dglShortestDistance( & graph , & nDistance , from , to , clipper , pclipctx , & spCache );

		if ( nret == 0 )
		{
			if ( dglErrno(& graph) == 0 )
			{
				printf( "destination node is unreachable\n\n" );
			}
		}
		else if ( nret < 0 ) 
		{
			fprintf( stderr , "dglShortestDistance error: %s\n", dglStrerror( & graph ) );
		}
		else
		{
			printf( "shortest distance: %ld\n" , nDistance );
		}
	}

	dglReleaseSPCache( & graph, & spCache );
	dglRelease( & graph );

	return 0;

}
